//
//  ChartsDefine.swift
//  TimeBoat
//
//  Created by bo wang on 2022/8/23.
//  Copyright © 2022 Time Boat. All rights reserved.
//

import Foundation

enum ChartViewType {
    case line
    case bar
    
    static let `default` = ChartViewType.line
}

/// 图表的类型
enum ChartsSubject {
    /// 热量
    case calories
    /// 距离
    case distance
    /// 步数
    case steps
    /// 心率
    case heartRate
    /// 睡眠
    case sleep
    /// 血压
    case bloodPressure
    /// 血氧
    case bloodOxygen
    /// 体温
    case temperature
    /// 运动
    case sports
    
    /// 页面标题
    var title: String {
        switch self {
        case .calories: return "热量"
        case .distance: return "距离"
        case .steps: return "步数"
        case .heartRate: return "心率"
        case .sleep: return "睡眠"
        case .bloodPressure: return "血压"
        case .bloodOxygen: return "血氧"
        case .temperature: return "温度"
        case .sports: return "所有运动"
        }
    }
    
    /// 可选择的时间粒度，不能返回空数组
    var granularities: [DateGranularity] {
        switch self {
        case .calories, .distance, .steps, .heartRate,
             .sleep, .bloodPressure, .bloodOxygen, .temperature:
            return DateGranularity.allCases.sorted(by: <)
        case .sports:
            return [.week, .month, .year]
        }
    }
    
    /// 默认选择的时间粒度
    var defaultGranularity: DateGranularity {
        granularities.first!
    }
}

enum DateGranularity: Int, CaseIterable, Comparable {
    
    /// 粒度为天，x轴范围为 0点到24点的每小时
    case day = 0
    /// 粒度为周，x轴范围为周一到周日的每天
    case week
    /// 粒度为月，x轴范围为当月的每天，结尾以日历真实的为准
    case month
    /// 粒度为年，x轴范围为1到12月
    case year
    
    /// 显示在 UISegmentControl 上的标题
    var title: String {
        switch self {
        case .day: return "日"
        case .week: return "周"
        case .month: return "月"
        case .year: return "年"
        }
    }
    
    static func < (lhs: DateGranularity, rhs: DateGranularity) -> Bool {
        lhs.rawValue < rhs.rawValue
    }
}

extension Date {
    
    /// self时间所在granularity的粒度下的起点时间
    func startDate(with granularity: DateGranularity) -> Date {
        switch granularity {
        case .day:
            return dayStart
        case .week:
            return startOfWeek.dayStart
        case .month:
            return startDayOfMonth.dayStart
        case .year:
            return startDayOfYear.dayStart
        }
    }
    
    /// self时间所在granularity的粒度下的结束时间
    func endDate(with granularity: DateGranularity) -> Date {
        let nextGranStart = nextDate(with: granularity).startDate(with: granularity)
        return Calendar.default.date(byAdding: .second, value: -1, to: nextGranStart)!
    }
    
    /// 在 granularity 的时间粒度下的下一个时间
    func nextDate(with granularity: DateGranularity) -> Date {
        let date = Calendar.default.startOfDay(for: self).addingTimeInterval(TimeInterval(TimeZone.current.secondsFromGMT()))
        var newDate: Date!
        switch granularity {
        case .day:
            newDate = Calendar.default.date(byAdding: .day, value: 1, to: date)
        case .week:
            newDate = Calendar.default.date(byAdding: .day, value: 7, to: date)
        case .month:
            newDate = Calendar.default.date(byAdding: .month, value: 1, to: date)
        case .year:
            newDate = Calendar.default.date(byAdding: .year, value: 1, to: date)
        }
        return newDate!
    }
    
    /// 在 granularity 的时间粒度下的前一个时间
    func previousDate(with granularity: DateGranularity) -> Date {
        let date = Calendar.default.startOfDay(for: self).addingTimeInterval(TimeInterval(TimeZone.current.secondsFromGMT()))
        var newDate: Date!
        switch granularity {
        case .day:
            newDate = Calendar.default.date(byAdding: .day, value: -1, to: date)
        case .week:
            newDate = Calendar.default.date(byAdding: .day, value: -7, to: date)
        case .month:
            newDate = Calendar.default.date(byAdding: .month, value: -1, to: date)
        case .year:
            newDate = Calendar.default.date(byAdding: .year, value: -1, to: date)
        }
        return newDate!
    }
    
    /// 在 granularity 的尺度上，self 是否比 date更早
    func isEarly(than date: Date, granularity: DateGranularity) -> Bool {
        let lhs = self.yearMonthDay
        let rhs = date.yearMonthDay
        switch granularity {
        case .day:
            return lhs.0 < rhs.0 || lhs.1 < rhs.1 || lhs.2 < rhs.2
        case .week:
            let ly = Calendar.default.component(.yearForWeekOfYear, from: self)
            let ry = Calendar.default.component(.yearForWeekOfYear, from: date)
            if ly == ry {
                return weekOfYear < date.weekOfYear
            }
            return ly < ry
        case .month:
            return lhs.0 < rhs.0 || lhs.1 < rhs.1
        case .year:
            return lhs.0 < rhs.0
        }
    }
    
    /// 在 granularity 的尺度上，self 是否和 date 相同
    func isSame(with date: Date, granularity: DateGranularity) -> Bool {
        let lhs = self.yearMonthDay
        let rhs = date.yearMonthDay
        switch granularity {
        case .day:
            return lhs.0 == rhs.0 && lhs.1 == rhs.1 && lhs.2 == rhs.2
        case .week:
            let ly = Calendar.default.component(.yearForWeekOfYear, from: self)
            let ry = Calendar.default.component(.yearForWeekOfYear, from: date)
            if ly == ry {
                return weekOfYear == date.weekOfYear
            }
            return false
        case .month:
            return lhs.0 == rhs.0 || lhs.1 == rhs.1
        case .year:
            return lhs.0 == rhs.0
        }
    }
    
    /// 在 granularity 的尺度上，self 是否比 date更晚
    func isLater(than date: Date, granularity: DateGranularity) -> Bool {
        return !isEarly(than: date, granularity: granularity) && !isSame(with: date, granularity: granularity)
    }
}

// 这是 func nextDate(with granularity: DateGranularity) -> Date 的测试函数
func testNextDateFuncs() {
    
    // 从1980年到2050年的每一天都测试一遍
    let testStartDay = Calendar.default.date(from: DateComponents(year: 1980, month: 1, day: 1, hour: 0, minute: 0, second: 0))!
    
    (0..<(70 * 365)).forEach { i in
        let date = Calendar.default.date(byAdding: .day, value: i, to: testStartDay)!
        let r1 = date.nextDate(with: .day)
        let r2 = date.nextDate(with: .week)
        let r3 = date.nextDate(with: .month)
        let r4 = date.nextDate(with: .year)
        
        // 检查 day
        if r1.year != date.year {
            assert(r1.year - date.year == 1)
            assert(r1.month == 1 && r1.day == 1)
            assert(date.month == 12 && date.day == 31)
        } else {
            let dayInYear = Calendar.default.ordinality(of: .day, in: .year, for: date)!
            let dayInYear1 = Calendar.default.ordinality(of: .day, in: .year, for: r1)!
            assert(dayInYear1 - dayInYear == 1)
        }
        
        // 检查 week
        if Calendar.default.component(.yearForWeekOfYear, from: date) != Calendar.default.component(.yearForWeekOfYear, from: r2) {
            // 不在同一个周年
            assert(r2.weekOfYear == 1)
            assert(date.weekOfYear == 52 || date.weekOfYear == 53)
        } else {
            assert(r2.weekOfYear - date.weekOfYear == 1)
        }
        
        // 检查月
        if date.year == r3.year {
            assert(r3.month - date.month == 1)
        } else {
            assert(r3.year - date.year == 1)
            assert(r3.month == 1)
            assert(date.month == 12)
        }
        
        // 检查年
        assert(r4.year - date.year == 1)
    }
    
    print("Test Passed!")
}

// 这是 func previousDate(with granularity: DateGranularity) -> Date 的测试函数
func testPreviousDateFuncs() {
    
    // 从1980年到2050年的每一天都测试一遍
    let testStartDay = Calendar.default.date(from: DateComponents(year: 1980, month: 1, day: 1, hour: 0, minute: 0, second: 0))!
    
    (0..<(70 * 365)).forEach { i in
        let date = Calendar.default.date(byAdding: .day, value: i, to: testStartDay)!
        let r1 = date.previousDate(with: .day)
        let r2 = date.previousDate(with: .week)
        let r3 = date.previousDate(with: .month)
        let r4 = date.previousDate(with: .year)
        
        // 检查 day
        if r1.year != date.year {
            assert(r1.year - date.year == -1)
            assert(date.month == 1 && date.day == 1)
            assert(r1.month == 12 && r1.day == 31)
        } else {
            let dayInYear = Calendar.default.ordinality(of: .day, in: .year, for: date)!
            let dayInYear1 = Calendar.default.ordinality(of: .day, in: .year, for: r1)!
            assert(dayInYear1 - dayInYear == -1)
        }
        
        // 检查 week
        if Calendar.default.component(.yearForWeekOfYear, from: date) != Calendar.default.component(.yearForWeekOfYear, from: r2) {
            // 不在同一个周年
            assert(date.weekOfYear == 1)
            assert(r2.weekOfYear == 52 || r2.weekOfYear == 53)
        } else {
            assert(r2.weekOfYear - date.weekOfYear == -1)
        }
        
        // 检查月
        if date.year == r3.year {
            assert(date.month - r3.month == 1)
        } else {
            assert(date.year - r3.year == 1)
            assert(date.month == 1)
            assert(r3.month == 12)
        }
        
        // 检查年
        assert(r4.year - date.year == -1)
    }
    
    print("Test Passed!")
}
